]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Add a SelectArea() DBus method
authorCosimo Cecchi <cosimoc@gnome.org>
Fri, 9 Nov 2012 01:19:19 +0000 (20:19 -0500)
committerCosimo Cecchi <cosimoc@gnome.org>
Thu, 3 Jan 2013 11:56:45 +0000 (12:56 +0100)
This will be useful for e.g. selecting an area for a screenshot.

https://bugzilla.gnome.org/show_bug.cgi?id=687954

js/Makefile.am
js/ui/selectArea.js [new file with mode: 0644]
js/ui/shellDBus.js
src/shell-global.c
src/shell-global.h

index d5d8047f8a0c89ec5ebc869188da8c0757af2829..7606f32b112d66887ef8b1a5946c1e248d32fc6e 100644 (file)
@@ -78,6 +78,7 @@ nobase_dist_js_DATA =         \
        ui/scripting.js         \
        ui/search.js            \
        ui/searchDisplay.js     \
+       ui/selectArea.js        \
        ui/shellDBus.js         \
        ui/status/accessibility.js      \
        ui/status/keyboard.js   \
diff --git a/js/ui/selectArea.js b/js/ui/selectArea.js
new file mode 100644 (file)
index 0000000..b5bbdc7
--- /dev/null
@@ -0,0 +1,134 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Cairo = imports.cairo;
+const Clutter = imports.gi.Clutter;
+const Gdk = imports.gi.Gdk;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const Shell = imports.gi.Shell;
+const Signals = imports.signals;
+const St = imports.gi.St;
+
+const Main = imports.ui.main;
+const Tweener = imports.ui.tweener;
+
+const SelectArea = new Lang.Class({
+    Name: 'SelectArea',
+
+    _init: function() {
+        this._startX = -1;
+        this._startY = -1;
+        this._lastX = 0;
+        this._lastY = 0;
+
+        this._initRubberbandColors();
+
+        this._group = new St.Widget({ visible: false,
+                                      reactive: true,
+                                      x: 0,
+                                      y: 0 });
+        Main.uiGroup.add_actor(this._group);
+
+        this._group.connect('button-press-event',
+                            Lang.bind(this, this._onButtonPress));
+        this._group.connect('button-release-event',
+                            Lang.bind(this, this._onButtonRelease));
+        this._group.connect('key-press-event',
+                            Lang.bind(this, this._onKeyPress));
+        this._group.connect('motion-event',
+                            Lang.bind(this, this._onMotionEvent));
+
+        let constraint = new Clutter.BindConstraint({ source: global.stage,
+                                                      coordinate: Clutter.BindCoordinate.ALL });
+        this._group.add_constraint(constraint);
+
+        this._rubberband = new Clutter.Rectangle({ color: this._background,
+                                                   has_border: true,
+                                                   border_width: 1,
+                                                   border_color: this._border });
+        this._group.add_actor(this._rubberband);
+    },
+
+    show: function() {
+        if (!Main.pushModal(this._group) || this._group.visible)
+            return;
+
+        global.set_cursor(Shell.Cursor.CROSSHAIR);
+        this._group.visible = true;
+    },
+
+    _initRubberbandColors: function() {
+        function colorFromRGBA(rgba) {
+            return new Clutter.Color({ red: rgba.red * 255,
+                                       green: rgba.green * 255,
+                                       blue: rgba.blue * 255,
+                                       alpha: rgba.alpha * 255 });
+        }
+
+        let path = new Gtk.WidgetPath();
+        path.append_type(Gtk.IconView);
+
+        let context = new Gtk.StyleContext();
+        context.set_path(path);
+        context.add_class('rubberband');
+
+        this._background = colorFromRGBA(context.get_background_color(Gtk.StateFlags.NORMAL));
+        this._border = colorFromRGBA(context.get_border_color(Gtk.StateFlags.NORMAL));
+    },
+
+    _getGeometry: function() {
+        return { x: Math.min(this._startX, this._lastX),
+                 y: Math.min(this._startY, this._lastY),
+                 width: Math.abs(this._startX - this._lastX),
+                 height: Math.abs(this._startY - this._lastY) };
+    },
+
+    _onKeyPress: function(actor, event) {
+        if (event.get_key_symbol() == Clutter.Escape)
+            this._destroy(null, false);
+
+        return;
+    },
+
+    _onMotionEvent: function(actor, event) {
+        if (this._startX == -1 || this._startY == -1)
+            return false;
+
+        [this._lastX, this._lastY] = event.get_coords();
+        let geometry = this._getGeometry();
+
+        this._rubberband.set_position(geometry.x, geometry.y);
+        this._rubberband.set_size(geometry.width, geometry.height);
+
+        return false;
+    },
+
+    _onButtonPress: function(actor, event) {
+        [this._startX, this._startY] = event.get_coords();
+        this._rubberband.set_position(this._startX, this._startY);
+
+        return false;
+    },
+
+    _onButtonRelease: function(actor, event) {
+        this._destroy(this._getGeometry(), true);
+        return false;
+    },
+
+    _destroy: function(geometry, fade) {
+        Tweener.addTween(this._group,
+                         { opacity: 0,
+                           time: fade ? 0.2 : 0,
+                           transition: 'easeOutQuad',
+                           onComplete: Lang.bind(this,
+                               function() {
+                                   Main.popModal(this._group);
+                                   this._group.destroy();
+                                   global.unset_cursor();
+
+                                   this.emit('finished', geometry);
+                               })
+                         });
+    }
+});
+Signals.addSignalMethods(SelectArea.prototype);
index 6b88baf252e922af3ba2541b60150b5586c1475f..1eba2960496e87384bb20e09942fe8b72cc91d21 100644 (file)
@@ -11,6 +11,7 @@ const ExtensionDownloader = imports.ui.extensionDownloader;
 const ExtensionUtils = imports.misc.extensionUtils;
 const Flashspot = imports.ui.flashspot;
 const Main = imports.ui.main;
+const SelectArea = imports.ui.selectArea;
 
 const GnomeShellIface = <interface name="org.gnome.Shell">
 <method name="Eval">
@@ -40,6 +41,12 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
     <arg type="s" direction="in" name="filename"/>
     <arg type="b" direction="out" name="success"/>
 </method>
+<method name="SelectArea">
+    <arg type="i" direction="out" name="x"/>
+    <arg type="i" direction="out" name="y"/>
+    <arg type="i" direction="out" name="width"/>
+    <arg type="i" direction="out" name="height"/>
+</method>
 <method name="FlashArea">
     <arg type="i" direction="in" name="x"/>
     <arg type="i" direction="in" name="y"/>
@@ -182,6 +189,23 @@ const GnomeShell = new Lang.Class({
                                     flash, invocation));
     },
 
+    SelectAreaAsync: function (params, invocation) {
+        let selectArea = new SelectArea.SelectArea();
+        selectArea.show();
+        selectArea.connect('finished', Lang.bind(this,
+            function(selectArea, areaRectangle) {
+                if (areaRectangle) {
+                    let retval = GLib.Variant.new('(iiii)',
+                        [areaRectangle.x, areaRectangle.y,
+                         areaRectangle.width, areaRectangle.height]);
+                    invocation.return_value(retval);
+                } else {
+                    invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
+                        "Operation was cancelled");
+                }
+            }));
+    },
+
     FlashArea: function(x, y, width, height) {
         let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: width, height: height});
         flashspot.fire();
index b7198a97854e876e48a55b9d0f7a35232665507d..10b1fc1d3aa5a3bc63660cc0442f78daf7716b0b 100644 (file)
@@ -616,6 +616,9 @@ shell_global_set_cursor (ShellGlobal *global,
     case SHELL_CURSOR_POINTING_HAND:
       name = "hand";
       break;
+    case SHELL_CURSOR_CROSSHAIR:
+      name = "crosshair";
+      break;
     default:
       g_return_if_reached ();
     }
@@ -638,6 +641,9 @@ shell_global_set_cursor (ShellGlobal *global,
         case SHELL_CURSOR_POINTING_HAND:
           cursor_type = GDK_HAND2;
           break;
+        case SHELL_CURSOR_CROSSHAIR:
+          cursor_type = GDK_CROSSHAIR;
+          break;
         case SHELL_CURSOR_DND_UNSUPPORTED_TARGET:
           cursor_type = GDK_X_CURSOR;
           break;
index 95c18e0954bd523b87225e14020a9e386539530b..7c14892ddc2b2cfa9b573b821a350049d395bfa5 100644 (file)
@@ -63,7 +63,8 @@ typedef enum {
   SHELL_CURSOR_DND_UNSUPPORTED_TARGET,
   SHELL_CURSOR_DND_MOVE,
   SHELL_CURSOR_DND_COPY,
-  SHELL_CURSOR_POINTING_HAND
+  SHELL_CURSOR_POINTING_HAND,
+  SHELL_CURSOR_CROSSHAIR
 } ShellCursor;
 
 void    shell_global_set_cursor              (ShellGlobal         *global,